﻿using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using EnetaMvc.ApplicationServices.Dto;
using EnetaMvc.Core.Domain.Content;
using EnetaMvc.Core.Domain.News;
using EnetaMvc.Core.Repositories;
using EnetaMvc.Infrastructure;
using System.Diagnostics;

namespace EnetaMvc.ApplicationServices
{
    public class NewsService : INewsService
    {
        private readonly IArticleRepository _articleRepository;
        private readonly IBlogRepository _blogRepository;
        private readonly IBlogPostRepository _blogPostRepository;
        private readonly ICommentRepository _commentRepository;
        private readonly IContentItemRepository _contentItemRepository;
        private readonly IPublishStateRepository _publishStateRepository;
        private readonly ITagRepository _tagRepository;
        private readonly IUserRepository _userRepository;

        public NewsService(IArticleRepository articleRepository, 
                           IBlogRepository blogRepository,
                           IBlogPostRepository blogPostRepository,
                           ICommentRepository commentRepository,
                           IContentItemRepository contentItemRepository,
                           IPublishStateRepository publishStateRepository, 
                           ITagRepository tagRepository,
                           IUserRepository userRepository)
        {
            
            _articleRepository = articleRepository;
            _blogRepository = blogRepository;
            _blogPostRepository = blogPostRepository;
            _commentRepository = commentRepository;
            _contentItemRepository = contentItemRepository;
            _publishStateRepository = publishStateRepository;
            _tagRepository = tagRepository;
            _userRepository = userRepository;

            Mapper.CreateMap<Tag, TagDto>();
            Mapper.CreateMap<Comment, CommentDto>();
            Mapper.CreateMap<NewsItem, NewsItemDto>()
                  .Include<Article, ArticleDto>()
                  .Include<BlogPost, BlogPostDto>()
                  .ForMember(a => a.Comments, m => m.MapFrom(c => c.Comments.Cast<Comment>().ToArray()))
                  .ForMember(a => a.Tags, m => m.MapFrom(c => c.Tags.Cast<Tag>().ToArray()));
            Mapper.CreateMap<Article, ArticleDto>();
            Mapper.CreateMap<BlogPost, BlogPostDto>();
        }

        public PagedResult<ArticleDto> ListPublishedArticles(int page, int pageSize)
        {
            var articles = _articleRepository.ListPublishedArticles(page, pageSize);
            var articleResult = new PagedResult<ArticleDto>();

            var articleDtos = new List<ArticleDto>();
            
            CreateArticleMap();
            Mapper.Map(articles.Results, articleDtos);

            Mapper.CreateMap(articles.GetType(), articleResult.GetType())
                  .ForMember("Results", m => m.Ignore());
            Mapper.Map(articles, articleResult);

            articleResult.Results = articleDtos;

            return articleResult;
        }


        public ArticleDto GetPublishedArticleById(long id)
        {
            var article = _articleRepository.GetPublishedArticleById(id);
            if (article == null)
                return null;

            CreateArticleMap();

            var articleDto = new ArticleDto();
            Mapper.Map(article, articleDto);

            return articleDto;
        }

        public ArticleDto GetArticleById(long id)
        {
            var article = _articleRepository.GetArticleById(id);
            if (article == null)
                return null;

            CreateArticleMap();

            var articleDto = new ArticleDto();
            Mapper.Map(article, articleDto);

            return articleDto;
        }

        private void CreateArticleMap()
        {
            Mapper.CreateMap<Tag, TagDto>();
            Mapper.CreateMap<Comment, CommentDto>();
            Mapper.CreateMap<Article, ArticleDto>()
                      .ForMember(a => a.Comments, m => m.MapFrom(c => c.Comments.ToArray()))
                      .ForMember(a => a.Tags, m => m.MapFrom(c => c.Tags.ToArray()));
        }


        public void SaveArticle(ArticleDto articleDto)
        {
            var article = new Article();
            if (articleDto.Id != 0)
                article = _articleRepository.GetArticleById(articleDto.Id);

            Mapper.CreateMap<ArticleDto, Article>()
                  .ForMember(a => a.CreatedBy, m => m.Ignore())
                  .ForMember(a => a.Comments, m => m.Ignore())
                  .ForMember(a => a.PublishState, m => m.Ignore())
                  .ForMember(a => a.PublishStateBy, m => m.Ignore())
                  .ForMember(a => a.Tags, m => m.Ignore());
            Mapper.Map(articleDto, article);

            if (article.Id == 0)
                article.CreatedBy = _userRepository.GetUserById(articleDto.CreatedById);

            article.PublishState = _publishStateRepository.GetPublishStateById(articleDto.PublishStateId);
            article.PublishStateBy = _userRepository.GetUserById(articleDto.PublishStateById);

            if (articleDto.SaveTagIds != null)
            {
                article.Tags.Clear();
                foreach (var tagId in articleDto.SaveTagIds)
                {
                    var tag = _tagRepository.GetTagById(tagId);
                    article.Tags.Add(tag);
                }
            }

            _articleRepository.SaveArticle(article);
        }

        public PagedResult<BlogPostDto> ListPublishedBlogPosts(int page, int pageSize)
        {
            var postsResult = _blogPostRepository.ListPublishedBlogPosts(page, pageSize);
            var postDtosResult = GetBlogPostDtosResult(postsResult);

            return postDtosResult;
        }
        
        public PagedResult<BlogPostDto> ListPublishedPostsForBlog(long blogId, int page, int pageSize)
        {
            var postsResult = _blogPostRepository.ListPublishedPostsForBlog(blogId, page, pageSize);
            var postDtosResult = GetBlogPostDtosResult(postsResult);

            return postDtosResult;
        }

        private static PagedResult<BlogPostDto> GetBlogPostDtosResult(PagedResult<BlogPost> postsResult)
        {
            var postDtosResult = new PagedResult<BlogPostDto>();
            var postDtosList = new List<BlogPostDto>();

            Mapper.CreateMap<Tag, TagDto>();
            Mapper.CreateMap<Comment, CommentDto>();
            Mapper.CreateMap<BlogPost, BlogPostDto>()
                      .ForMember(a => a.Comments, m => m.MapFrom(c => c.Comments.ToArray()))
                      .ForMember(a => a.Tags, m => m.MapFrom(c => c.Tags.ToArray()));

            Mapper.Map(postsResult.Results, postDtosList);

            Mapper.CreateMap(postsResult.GetType(), postDtosResult.GetType())
                  .ForMember("Results", m => m.Ignore());
            Mapper.Map(postsResult, postDtosResult);

            postDtosResult.Results = postDtosList;
            return postDtosResult;
        }

        public BlogDto GetPublishedBlogById(long id)
        {
            var blog = _blogRepository.GetPublishedBlogById(id);
            if (blog == null)
                return null;

            var blogDto = new BlogDto();

            Mapper.CreateMap<Blog, BlogDto>();
            Mapper.Map(blog, blogDto);

            return blogDto;
        }

        public BlogDto GetBlogById(long id)
        {
            var blog = _blogRepository.GetBlogById(id);
            if (blog == null)
                return null;

            var blogDto = new BlogDto();

            Mapper.CreateMap<Blog, BlogDto>();
            Mapper.Map(blog, blogDto);

            return blogDto;
        }

        public PagedResult<BlogPostDto> ListPostsForBlog(long blogId, int page, int pageSize)
        {
            var posts = _blogPostRepository.ListPostsForBlog(blogId, page, pageSize);
            var postDtos = GetBlogPostDtosResult(posts);

            return postDtos;
        }


        public BlogPostDto GetPublishedBlogPostById(long id)
        {
            var blogPost = _blogPostRepository.GetPublishedBlogPost(id);
            if (blogPost == null)
                return null;

            return GetBlogPostDto(blogPost);
        }

        private static BlogPostDto GetBlogPostDto(BlogPost blogPost)
        {
            var blogPostDto = new BlogPostDto();

            Mapper.CreateMap<Tag, TagDto>();
            Mapper.CreateMap<Comment, CommentDto>();
            Mapper.CreateMap<BlogPost, BlogPostDto>()
                  .ForMember(b => b.Comments, m => m.MapFrom(b => b.Comments.ToArray()))
                  .ForMember(b => b.Tags, m => m.MapFrom(b => b.Tags.ToArray()));

            Mapper.Map(blogPost, blogPostDto);
            return blogPostDto;
        }

        public BlogPostDto GetBlogPostById(long id)
        {
            var blogPost = _blogPostRepository.GetBlogPostById(id);
            if (blogPost == null)
                return null;

            return GetBlogPostDto(blogPost);
        }

        public void SaveBlogPost(BlogPostDto blogPostDto)
        {
            if (blogPostDto == null)
                return;

            var blogPost = new BlogPost();
            if (blogPostDto.Id > 0)
                blogPost = _blogPostRepository.GetBlogPostById(blogPostDto.Id);

            Mapper.CreateMap<BlogPostDto, BlogPost>()
                  .ForMember(b => b.Blog, m => m.Ignore())
                  .ForMember(b => b.Comments, m => m.Ignore())
                  .ForMember(b => b.CreatedBy, m => m.Ignore())
                  .ForMember(b => b.PublishState, m => m.Ignore())
                  .ForMember(b => b.PublishStateBy, m => m.Ignore())
                  .ForMember(b => b.Tags, m => m.Ignore());
            Mapper.Map(blogPostDto, blogPost);

            blogPost.Blog = _blogRepository.GetBlogById(blogPostDto.BlogId);
            blogPost.CreatedBy = _userRepository.GetUserById(blogPostDto.CreatedById);
            blogPost.PublishState = _publishStateRepository.GetPublishStateById(blogPostDto.PublishStateId);
            blogPost.PublishStateBy = _userRepository.GetUserById(blogPostDto.PublishStateById);

            if (blogPostDto.SaveTagIds != null)
            {
                blogPost.Tags.Clear();
                foreach (var tagId in blogPostDto.SaveTagIds)
                {
                    var tag = _tagRepository.GetTagById(tagId);
                    blogPost.Tags.Add(tag);
                }
            }

            _blogPostRepository.SaveBlogPost(blogPost);
        }

        public void SaveBlog(BlogDto blogDto)
        {
            if (blogDto == null)
                return;

            var blog = new Blog();
            if (blogDto.Id > 0)
                blog = _blogRepository.GetBlogById(blogDto.Id);

            Mapper.CreateMap<BlogDto, Blog>()
                  .ForMember(b => b.CreatedBy, m => m.Ignore())
                  .ForMember(b => b.PublishingState, m => m.Ignore())
                  .ForMember(b => b.PublishingStateBy, m => m.Ignore());
            Mapper.Map(blogDto, blog);

            blog.CreatedBy = _userRepository.GetUserById(blogDto.CreatedById);
            blog.PublishingState = _publishStateRepository.GetPublishStateById(blogDto.PublishingStateId);
            blog.PublishingStateBy = _userRepository.GetUserById(blogDto.PublishingStateById);

            _blogRepository.SaveBlog(blog);
        }

        public IList<BlogDto> ListPublishedBlogs()
        {
            var blogs = _blogRepository.ListPublishedBlogs();
            var blogDtos = new List<BlogDto>();

            Mapper.CreateMap<Blog, BlogDto>();
            Mapper.Map(blogs, blogDtos);

            return blogDtos;
        }

        public PagedResult<NewsItemDto> ListPublishedNewsItems(int page, int pageSize)
        {
            var items = _contentItemRepository.ListContentItems<NewsItem>(page, pageSize);
            var itemDtos = new PagedResult<NewsItemDto>();
            var itemDtoList = new List<NewsItemDto>();



            Mapper.CreateMap(items.GetType(), itemDtos.GetType())
                  .ForMember("Results", m => m.Ignore());
            Mapper.Map(items, itemDtos);
            Mapper.Map(items.Results, itemDtoList);

            itemDtos.Results = itemDtoList;
            return itemDtos;

        }

        public bool BlogPostExists(string infoSourceUrl, long blogId)
        {
            return _blogPostRepository.BlogPostExists(infoSourceUrl, blogId);
        }

        public IList<BlogDto> ListPendingBlogs()
        {
            var blogs = _blogRepository.ListPendingBlogs();
            var blogDtos = new List<BlogDto>();

            Mapper.Map(blogs, blogDtos);

            return blogDtos;
        }

        public IList<BlogPostDto> ListPendingBlogPosts(int rowLimit)
        {
            var blogPosts = _contentItemRepository.ListPendingContentItems<BlogPost>(rowLimit);
            var blogPostDtos = new List<BlogPostDto>();

            Mapper.Map(blogPosts, blogPostDtos);

            return blogPostDtos;
        }

        public IList<ArticleDto> ListPendingArticles()
        {
            var articles = _contentItemRepository.ListPendingContentItems<Article>(20);
            var articleDtos = new List<ArticleDto>();

            Mapper.Map(articles, articleDtos);

            return articleDtos;
        }

        public IList<BlogDto> ListBlogsForUser(string token)
        {
            var blogs = _blogRepository.ListBlogsForUser(token);

            var blogList = new List<BlogDto>();

            Mapper.Map(blogs, blogList);

            return blogList;
        }
    }
}
